#include "GLFunction3DGraph.h"
#include "GLRenderingContext.h"
using namespace OpenGLWrapper;

GLFunction3DGraph::GLFunction3DGraph()
	: GLMath3DApplication()
{
	_graphsColors.clear();
	_numGraphs.clear();
	_graphsX.clear();
	_graphsY.clear();
	_graphsZ.clear();

	_linesColors.clear();
	_linesX.clear();
	_linesY.clear();
	_linesZ.clear();

	_pointsColors.clear();
	_pointsX.clear();
	_pointsY.clear();
	_pointsZ.clear();

	_vectors.clear();
	_vectorsColors.clear();

	strcpy_s(_currentDisplayedPoint, "");
	_textX = _textY = _textZ = 0;
}

GLFunction3DGraph::~GLFunction3DGraph()
{
}

bool GLFunction3DGraph::Init()
{
	if (!GLMath3DApplication::Init())
		return false;

	return true;
}
void GLFunction3DGraph::Prepare(float dt)
{
	GLMath3DApplication::Prepare(dt);
}

void GLFunction3DGraph::RenderFunctions()
{
	// draw selected points
	auto renderingContext = GLRenderingContext::GetCurrentContext();
	renderingContext->RenderText(_textX, _textY, _textZ, _currentDisplayedPoint, Color(1.0, 1.0, 1.0), _fontSize);

	// draw functions
	for (size_t j = 0; j < _numGraphs.size(); j++)
	{
		glBegin(GL_POINTS);
		glColor3d(_graphsColors[j].r, _graphsColors[j].g, _graphsColors[j].b);
		for (int i = 0; i < _numGraphs[j]; i++)
			glVertex3d(_graphsX[j][i] * _stepX / _incX, _graphsY[j][i] * _stepY / _incY, _graphsZ[j][i] * _stepZ / _incZ);
		glEnd();
	}
}

void GLFunction3DGraph::RenderLines()
{
	glDisable( GL_LINE_SMOOTH );
	glLineWidth(2.0);
	glBegin(GL_LINES);
	for (size_t j = 0; j < _linesX.size(); j+=2)
	{
		glColor3d(_linesColors[j / 2].r, _linesColors[j / 2].g, _linesColors[j / 2].b);
		glVertex3d(_linesX[j] * _stepX / _incX, _linesY[j] * _stepY / _incY, _linesZ[j] * _stepZ / _incZ);
		glVertex3d(_linesX[j + 1] * _stepX / _incX, _linesY[j + 1] * _stepY / _incY, _linesZ[j + 1] * _stepZ / _incZ);
	}
	glEnd();
	glLineWidth(1.0);
	glEnable( GL_LINE_SMOOTH );
}

void GLFunction3DGraph::RenderPoints()
{
	glPointSize(10.0);
	glBegin(GL_POINTS);
	for (size_t j = 0; j < _pointsColors.size(); j++)
	{
		glColor3d(_pointsColors[j].r, _pointsColors[j].g, _pointsColors[j].b);
		glVertex3d(_pointsX[j] * _stepX / _incX, _pointsY[j] * _stepY / _incY, _pointsZ[j] * _stepZ / _incZ);
	}
	glEnd();
	glPointSize(1.0);
}

void GLFunction3DGraph::RenderVectors()
{
	glPointSize(10.0);
	glBegin(GL_POINTS);
	for (size_t j = 0; j < _vectors.size(); j++)
	{
		glColor3d(_vectorsColors[j].r, _vectorsColors[j].g, _vectorsColors[j].b);
		glVertex3d(_vectors[j].x * _stepX / _incX, _vectors[j].y * _stepY / _incY, _vectors[j].z * _stepZ / _incZ);
	}
	glEnd();
	glPointSize(1.0);

	glDisable( GL_LINE_SMOOTH );
	glLineWidth(2.0);
	glBegin(GL_LINES);
	for (size_t j = 0; j < _vectors.size(); j++)
	{
		glColor3d(_vectorsColors[j].r, _vectorsColors[j].g, _vectorsColors[j].b);
		glVertex3d(0, 0, 0);
		glVertex3d(_vectors[j].x * _stepX / _incX, _vectors[j].y * _stepY / _incY, _vectors[j].z * _stepZ / _incZ);
	}
	glEnd();
	glLineWidth(1.0);
	glEnable( GL_LINE_SMOOTH );
}

void GLFunction3DGraph::OnMathRender()
{
	RenderVectors();
	RenderFunctions();
	RenderLines();
	RenderPoints();
}

void GLFunction3DGraph::OnMouseDown(int button, int x, int y)
{
	GLMath3DApplication::OnMouseDown(button, x, y);
}

void GLFunction3DGraph::OnMouseMove(int x, int y)
{
	GLMath3DApplication::OnMouseMove(x, y);

	strcpy_s(_currentDisplayedPoint, "");
	if (GetKeyState(VK_LMENU) & 0x80)
	{
		POINT p = { x, y };
		ClientToScreen(GetActiveWindow(), &p);
		x = p.x; y = p.y;

		double world[16], proj[16];
		int viewport[4];
		glGetDoublev(GL_MODELVIEW_MATRIX, world);
		glGetDoublev(GL_PROJECTION_MATRIX, proj);
		glGetIntegerv(GL_VIEWPORT, viewport);
		gluUnProject(x, y, 0.0, world, proj, viewport, &_textX, &_textY, &_textZ);

		double objX = (_textX / _stepX) * _incX;
		double objY = (_textY / _stepY) * _incY;
		double objZ = (_textZ / _stepZ) * _incZ;
		sprintf_s(_currentDisplayedPoint, "(%.2g, %.2g, %.2g)", objX, objY, objZ);
	}
}

void GLFunction3DGraph::Render()
{
	GLMath3DApplication::Render();	
}

void GLFunction3DGraph::Shutdown()
{
	GLMath3DApplication::Shutdown();
}

void GLFunction3DGraph::OnResize(int width, int height)
{
	GLMath3DApplication::OnResize(width, height);
}

void GLFunction3DGraph::AddFunction(double(*f)(double x, double y), double sx, double ex, double sy, double ey, double stepx, double stepy, Color color, bool reversexyz, bool reverseyxz)
{
	_graphsColors.push_back(color);

	int countx = int((ex - sx) / stepx);
	int county = int((ex - sx) / stepy);

	_numGraphs.push_back(countx * county);
	std::vector<double> pointsX, pointsY, pointsZ;
	for (int i = 0; i < countx; i++)
	{
		for (int j = 0; j < county; j++)
		{
			pointsX.push_back(sx + i * stepx);
			pointsY.push_back(sy + j * stepy);
			pointsZ.push_back(f(sx + i * stepx, sy + j * stepy));
		}		
	}

	if (reversexyz)
	{
		_graphsX.push_back(pointsZ);
		_graphsY.push_back(pointsY);
		_graphsZ.push_back(pointsX);
	}
	else if (reverseyxz)
	{
		_graphsX.push_back(pointsZ);
		_graphsY.push_back(pointsZ);
		_graphsZ.push_back(pointsY);
	}
	else
	{
		_graphsX.push_back(pointsX);
		_graphsY.push_back(pointsY);
		_graphsZ.push_back(pointsZ);
	}
}

void GLFunction3DGraph::AddLine(double sx, double sy, double sz, double ex, double ey, double ez, Color color)
{
	_linesColors.push_back(color);
	_linesX.push_back(sx); _linesX.push_back(ex);
	_linesY.push_back(sy); _linesY.push_back(ey);
	_linesZ.push_back(sz); _linesZ.push_back(ez);
}

void GLFunction3DGraph::AddPoint(double sx, double sy, double sz, Color color)
{
	_pointsColors.push_back(color);
	_pointsX.push_back(sx); 
	_pointsY.push_back(sy);
	_pointsZ.push_back(sz);
}

void GLFunction3DGraph::AddVector(Vector3D vector, Color color)
{
	_vectors.push_back(vector);
	_vectorsColors.push_back(color);
}